﻿namespace HuwmanCode.ServiceProcess

open System.Security
open System.Security.Permissions
open System.Runtime.InteropServices
open Microsoft.Win32.SafeHandles
open System.Runtime.ConstrainedExecution

type FailureRecoveryAction =
| None = 0      // No action.
| Restart = 1   // Restart the service.
| Reboot = 2    // Reboot the computer.
| Notify = 3    // Run a command.

///<summary>
/// Represents an action that the service control manager can perform.
/// http://msdn.microsoft.com/en-us/library/ms685126(VS.85).aspx
///</summary>
[<Struct; StructLayout(LayoutKind.Sequential)>]
type ServiceFailureAction =
    val Action:FailureRecoveryAction
    val Delay:uint32 //The time to wait before performing the specified action, in milliseconds.

    new (action, delay) = 
        { Action = action; Delay = delay }

[<AutoOpen>]
module internal NativeMethods =
    let SC_MANAGER_ALL_ACCESS           = 0xF003F
    let SERVICE_ALL_ACCESS              = 0xF01FF
    let SERVICE_CONFIG_DESCRIPTION      = 0x1
    let SERVICE_CONFIG_FAILURE_ACTIONS  = 0x2
    let SERVICE_NO_CHANGE               = -1
    let ERROR_ACCESS_DENIED             = 5

    ///<summary>
    /// Retrieves the configuration parameters of the specified service.
    /// http://msdn.microsoft.com/en-us/library/ms684932(VS.85).aspx
    ///</summary>
    [<DllImport("advapi32.dll", EntryPoint="QueryServiceConfig", SetLastError=true)>] 
    extern bool QueryServiceConfig(nativeint handle, nativeint serviceConfigQuery, int bufferSize, int& bytesNeeded)

    ///<summary>
    /// Represents the action the service controller should take on each failure of a service.
    /// http://msdn.microsoft.com/en-us/library/ms685939(VS.85).aspx
    ///</summary>
    [<Struct; StructLayout(LayoutKind.Sequential)>]
    type ServiceFailureActions = 
        val mutable ResetPeriod : int //The time after which to reset the failure count to zero if there are no failures, in seconds. Specify INFINITE to indicate that this value should never be reset.
        [<MarshalAs (UnmanagedType.LPStr)>]
        val mutable RebootMessage : string
        [<MarshalAs (UnmanagedType.LPStr)>]
        val mutable Command : string
        val mutable ActionCount : int 
        val mutable ActionPtr : nativeint

        new (command, actionCount, actionPtr, resetPeriod, rebootMessage) =
            { ResetPeriod = resetPeriod;
              RebootMessage = rebootMessage;
              Command = command;
              ActionCount = actionCount;
              ActionPtr = actionPtr }

    ///<summary>
    /// Changes the optional configuration parameters of a service.
    /// http://msdn.microsoft.com/en-us/library/ms681988(VS.85).aspx
    ///</summary>
    [<DllImport("advapi32.dll", EntryPoint="ChangeServiceConfig2")>]
    extern bool ChangeServiceFailureActions(nativeint serviceHandle, int infoLevel, 
            [<MarshalAs (UnmanagedType.Struct)>] ServiceFailureActions& failureActions)

    ///<summary>
    /// Contains configuration information for an installed service.
    /// http://msdn.microsoft.com/en-us/library/ms684950(VS.85).aspx
    ///</summary>
    [<Struct; StructLayout(LayoutKind.Sequential)>]
    type ServiceConfigInfo =
        val mutable ServiceType : int
        val mutable StartType : int
        val mutable ErrorControl : int
        [<MarshalAs (UnmanagedType.LPStr)>]
        val mutable BinaryPathName : string
        [<MarshalAs (UnmanagedType.LPStr)>]
        val mutable LoadOrderGroup : string
        val mutable tagId : int
        [<MarshalAs (UnmanagedType.LPStr)>]
        val mutable Dependencies : string 
        [<MarshalAs (UnmanagedType.LPStr)>]
        val mutable ServiceStartName : string
        [<MarshalAs (UnmanagedType.LPStr)>]
        val mutable DisplayName : string

    ///<summary>
    /// Contains a service description.
    /// http://msdn.microsoft.com/en-us/library/ms685156(VS.85).aspx
    ///</summary>
    [<Struct; StructLayout(LayoutKind.Sequential)>]
    type ServiceDescription =
        [<MarshalAs (UnmanagedType.LPStr)>]
        val mutable Description : string

    ///<summary>
    /// Closes a handle to a service control manager or service object.
    /// http://msdn.microsoft.com/en-us/library/ms682028(VS.85).aspx
    ///</summary>
    [<DllImport("advapi32.dll")>]
    extern bool CloseServiceHandle(nativeint serviceControllerManagerHandle)
    
    [<SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode = true)>]
    [<SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)>]
    type internal SafeServiceHandle() =
        inherit SafeHandleZeroOrMinusOneIsInvalid(true)

        [<ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)>]
        override sh.ReleaseHandle() =
            CloseServiceHandle(sh.handle)

    ///<summary>
    /// Establishes a connection to the service control manager on the specified computer and opens the specified service control manager database.
    /// http://msdn.microsoft.com/en-us/library/ms684323(VS.85).aspx
    ///</summary>
    [<DllImport("advapi32.dll")>]
    extern SafeServiceHandle OpenSCManager([<MarshalAs (UnmanagedType.LPStr)>] string machineName, 
            [<MarshalAs (UnmanagedType.LPStr)>] string databaseName, int desiredAccess)

    ///<summary>
    /// Opens an existing service.
    /// http://msdn.microsoft.com/en-us/library/ms684330(VS.85).aspx
    ///</summary>
    [<DllImport("advapi32.dll")>]
    extern SafeServiceHandle OpenService(SafeServiceHandle scmHandle, 
            [<MarshalAs (UnmanagedType.LPStr)>] string serviceName, int desiredAccess);

    type ServiceConfig2InfoLevel =
    | Description = 0x00000001      // The buffer parameter is a pointer to a ServiceDescription structure.
    | FailureActions = 0x00000002   // The buffer parameter is a pointer to a ServiceFailureActions structure.

    [<DllImport("advapi32.dll")>]
    extern bool ChangeServiceConfig2(SafeServiceHandle handle, ServiceConfig2InfoLevel infoLevel, nativeint buffer)